package fr.lip6.meta.ple.generation.aspectj;

import java.util.ArrayList;

import org.eclipse.emf.common.util.EList;
import org.eclipse.gmt.modisco.java.ConstructorDeclaration;
import org.eclipse.gmt.modisco.java.Expression;
import org.eclipse.gmt.modisco.java.ExpressionStatement;
import org.eclipse.gmt.modisco.java.ForStatement;
import org.eclipse.gmt.modisco.java.IfStatement;
import org.eclipse.gmt.modisco.java.InfixExpression;
import org.eclipse.gmt.modisco.java.MethodDeclaration;
import org.eclipse.gmt.modisco.java.MethodInvocation;
import org.eclipse.gmt.modisco.java.ReturnStatement;
import org.eclipse.gmt.modisco.java.Statement;
import org.eclipse.gmt.modisco.java.WhileStatement;
import org.eclipse.gmt.modisco.java.emf.impl.AssignmentImpl;
import org.eclipse.gmt.modisco.java.emf.impl.BlockImpl;
import org.eclipse.gmt.modisco.java.emf.impl.ExpressionStatementImpl;
import org.eclipse.gmt.modisco.java.emf.impl.FieldAccessImpl;
import org.eclipse.gmt.modisco.java.emf.impl.ForStatementImpl;
import org.eclipse.gmt.modisco.java.emf.impl.IfStatementImpl;
import org.eclipse.gmt.modisco.java.emf.impl.InfixExpressionImpl;
import org.eclipse.gmt.modisco.java.emf.impl.MethodInvocationImpl;
import org.eclipse.gmt.modisco.java.emf.impl.ReturnStatementImpl;
import org.eclipse.gmt.modisco.java.emf.impl.SingleVariableAccessImpl;
import org.eclipse.gmt.modisco.java.emf.impl.WhileStatementImpl;

import fr.lip6.meta.ple.generation.fh.java.GenerateFromXmiFHJava;
import fr.lip6.meta.ple.generation.strategy.ProcessConstraintStrategy;

import artefact.umlClassDiagram.CreateOperation;

public class ProcessConstraintAJ implements ProcessConstraintStrategy {
	static ArrayList<String> variables = new ArrayList<String>();
	
	@Override
	public ArrayList<String> getOperationVariables(CreateOperation crOperation) {
		String[] lien;
		org.eclipse.gmt.modisco.java.Block b = null;
		
		variables.clear();
		lien = crOperation.getLien().split("::");
		String params = lien.length > 2 ? lien[2] : null;
		String[] paramsArray = params == null ? new String[0] : params.substring(1, params.length()-1).split(",");
		if (paramsArray.length == 1 && paramsArray[0].equals("")) paramsArray = new String[0];
		
		if (!crOperation.getName().equals(crOperation.getOwener())) { 
			MethodDeclaration md = GenerateFromXmiFHJava.getMethodDeclaration(lien[0], lien[1], crOperation.getOwener(), crOperation.getName(), paramsArray);
			b = md.getBody();
		} else {
			ConstructorDeclaration consDecl =
				GenerateFromXmiFHJava.getConstructorDeclaration(
					lien[0],
					lien[1],
					crOperation.getOwener(),
					crOperation.getName(),
					paramsArray
				);
			b = consDecl.getBody();
		}
		
		{
			EList<Statement> sts = b.getStatements();
			
			for(Statement st : sts) {
				getStatementVariables(st);
			}
		}
		
		return variables;
	}
	
	@Override
	public void getStatementVariables(Statement st)
	{
		if (st instanceof ExpressionStatement) {
			ExpressionStatementImpl es=(ExpressionStatementImpl) st;
			readExpressionStatement(es);
		}
	 
		if (st instanceof ReturnStatement) {
			ReturnStatementImpl es=(ReturnStatementImpl) st;
			getReturnStatementBody((ReturnStatement) es);
		}
		
		if (st instanceof IfStatement) {
			IfStatementImpl ifs=(IfStatementImpl) st;
			readIfStatement(ifs);
		}
		
		if (st instanceof ForStatement) {
			ForStatementImpl f=(ForStatementImpl) st;
			getForStatmentBody(f);
		}
		
		if (st instanceof WhileStatement) {
			WhileStatementImpl w=(WhileStatementImpl) st;
			getWhileStatmentBody(w);
		}
	}
	
	private void readExpressionStatement(ExpressionStatement es) {
		Expression ii =es.getExpression();
		readExpression(ii);
	}

	private void getReturnStatementBody(ReturnStatement es) {
		org.eclipse.gmt.modisco.java.Expression ex = es.getExpression();
		readExpression(ex);	
	}

	private void getForStatmentBody(ForStatement f) {
		for(Expression ex : f.getInitializers()) {
			readExpression(ex);
		}
		readExpression(f.getExpression());
		
		for(Expression ex : f.getUpdaters()) {
			readExpression(ex);
		}
		
		BlockImpl rst = (BlockImpl) f.getBody();
		for(Statement st : rst.getStatements()) {
			getStatementVariables(st);
		}		
	}

	private void getWhileStatmentBody(WhileStatementImpl w) {
		BlockImpl rst=(BlockImpl) w.getBody();

		for(Statement st : rst.getStatements()) {
			getStatementVariables(st);	
		}
	}

	private void readBlock(BlockImpl b) {
		for(Statement st: b.getStatements()) {
			getStatementVariables(st);	
		}
	}

	private void readIfStatement(IfStatement es) {
		readExpression(es.getExpression());
		
		if (es.getThenStatement() instanceof BlockImpl) {
			BlockImpl st1 = (BlockImpl) es.getThenStatement();
			readBlock(st1);
		} else {
			getStatementVariables(es.getThenStatement());	
		}
		
		if (es.getElseStatement() instanceof BlockImpl) {
			BlockImpl st = (BlockImpl) es.getElseStatement();
			readBlock(st);
		} else if (es.getElseStatement() == null) {
			
		} else {
			getStatementVariables(es.getElseStatement());
		}
	}

	private void readExpression(Expression ex) {
		if (ex instanceof InfixExpression) {
			InfixExpressionImpl res1 = (InfixExpressionImpl) ex;

			readExpression(res1.getLeftOperand());
			readExpression(res1.getRightOperand());
		}
		
		if (ex instanceof AssignmentImpl) {
			AssignmentImpl res1 = (AssignmentImpl) ex;

			readExpression(res1.getLeftHandSide());
			readExpression(res1.getRightHandSide());
		}

		if (ex instanceof FieldAccessImpl) {
			FieldAccessImpl leftside = (FieldAccessImpl) ex;
			variables.add(leftside.getField().getVariable().getName());
		}

		if (ex instanceof SingleVariableAccessImpl) {
			SingleVariableAccessImpl leftside = (SingleVariableAccessImpl) ex;
			if (!variables.contains(leftside.getVariable().getName()))
				variables.add(leftside.getVariable().getName());
			readExpression(leftside.getQualifier());
		}

		if (ex instanceof MethodInvocation) {
			MethodInvocationImpl mo = (MethodInvocationImpl) ex;
			
			EList<Expression> exs = mo.getArguments();
			
			for(Expression ex1 : exs) {
				readExpression(ex1);
			}
		}
	}
}